using System;
using System.Net;
using CodePlex.UnitTestUtility;
using Xunit;

namespace CodePlex.TfsLibrary.Utility
{
    public class CredentialsCacheTest
    {
        [Fact]
        public void ClearingCachedCredentialsDeletesCacheFile()
        {
            TestableCredentialsCache cache = TestableCredentialsCache.Create();
            cache["http://foo"] = new NetworkCredential("user", "pass", "domain");

            cache.Clear();

            Assert.False(cache.FileSystem.FileExists(cache.CacheFilename));
        }

        [Fact]
        public void KnownSiteReturnsValue()
        {
            TestableCredentialsCache cache = TestableCredentialsCache.Create();
            cache["http://foo"] = new NetworkCredential("user", "pass", "domain");

            NetworkCredential result = cache["http://foo"];

            Assert.Equal("user", result.UserName);
            Assert.Equal("pass", result.Password);
            Assert.Equal("domain", result.Domain);
        }

        [Fact]
        public void NullCredentialsForSetCredentialThrows()
        {
            TestableCredentialsCache cache = TestableCredentialsCache.Create();

            Assert.Throws<ArgumentNullException>(delegate
                                                 {
                                                     cache["http://foo"] = null;
                                                 });
        }

        [Fact]
        public void NullUrlForGetCredentialThrows()
        {
            TestableCredentialsCache cache = TestableCredentialsCache.Create();

            Assert.Throws<ArgumentNullException>(delegate
                                                 {
                                                     NetworkCredential unused = cache[null];
                                                 });
        }

        [Fact]
        public void NullUrlForSetCredentialThrows()
        {
            TestableCredentialsCache cache = TestableCredentialsCache.Create();

            Assert.Throws<ArgumentNullException>(delegate
                                                 {
                                                     cache[null] = new NetworkCredential();
                                                 });
        }

        [Fact]
        public void SetValuesAreCachedToDisk()
        {
            TestableCredentialsCache cache = TestableCredentialsCache.Create();

            cache["http://foo"] = new NetworkCredential("user", "pass", "domain");

            Assert.True(cache.FileSystem.FileExists(cache.CacheFilename));
        }

        [Fact]
        public void SetValuesCanBeReReadFromDisk()
        {
            TestableCredentialsCache cache = TestableCredentialsCache.Create();
            TestableCredentialsCache cache2 = TestableCredentialsCache.CreateWithExistingFileSystem(cache.FileSystem);
            cache["http://foo"] = new NetworkCredential("user", "pass", "domain");

            NetworkCredential result = cache2["http://foo"];

            Assert.Equal("user", result.UserName);
            Assert.Equal("pass", result.Password);
            Assert.Equal("domain", result.Domain);
        }

        [Fact]
        public void StoredCredentialsAreEncrypted()
        {
            TestableCredentialsCache cache = TestableCredentialsCache.Create();

            cache["http://foo"] = new NetworkCredential("user", "pass", "domain");

            CredentialsCacheEntries entries = cache.GetEntries();
            CredentialsCacheEntry entry = entries["http://foo"];
            Assert.NotEqual("user", entry.Username_Serialized);
            Assert.NotEqual("pass", entry.Password_Serialized);
            Assert.NotEqual("domain", entry.Domain_Serialized);
        }

        [Fact]
        public void UnknownSiteReturnsNullCredential()
        {
            TestableCredentialsCache cache = TestableCredentialsCache.Create();

            NetworkCredential result = cache["http://foo"];

            Assert.Null(result);
        }

        class TestableCredentialsCache : CredentialsCache
        {
            public readonly MockFileSystem FileSystem;

            public TestableCredentialsCache(MockFileSystem fileSystem)
                : base(fileSystem)
            {
                FileSystem = fileSystem;
            }

            public new string CacheFilename
            {
                get { return base.CacheFilename; }
            }

            public static TestableCredentialsCache Create()
            {
                return new TestableCredentialsCache(new MockFileSystem());
            }

            public static TestableCredentialsCache CreateWithExistingFileSystem(MockFileSystem fileSystem)
            {
                return new TestableCredentialsCache(fileSystem);
            }

            public CredentialsCacheEntries GetEntries()
            {
                return CredentialsCacheEntries.Deserialize(FileSystem, CacheFilename);
            }
        }
    }
}